home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr22 / whoapc.zip / WHOA!.ASM next >
Assembly Source File  |  1993-05-01  |  33KB  |  1,159 lines

  1. ;Name            WHOA
  2. ;Title            WHOA! Version 1.0 - Brad D Crandall, 1987
  3. ;            All rights reserved.
  4. ;------------------------------------------------------------------------------
  5. ;    WHOA!.asm    -    Brad D Crandall
  6. ;                
  7. ;
  8. ;    This program will slow a computer down so programs and
  9. ;    games can be examined closely. Designed to run in a Text
  10. ;    or graphic screen mode. To bring up WHOA!'s Menu,
  11. ;    press <Alt> <F10>.
  12. ;
  13. ;    Compiler:    MASM Version 2.0 or greater.
  14. ;    Computer:    IBM PC or Compatible running on MS-DOS or
  15. ;            PC-DOS Version 2.0 or greater. Also works
  16. ;            on IBM's Personal System/2s under DOS 3.3.
  17. ;
  18. ;    Compiling procedure:
  19. ;            masm WHOA!;
  20. ;            link WHOA!;
  21. ;            exe2bin WHOA! WHOA!.com
  22. ;
  23. ;    Notes:
  24. ;        Care should be taken when determining what value to use for
  25. ;        SET_MAX (located in EQUATE AREA). If the value is too large,  
  26. ;        a hard loop may result. The value 435 if optimum for a 12-MHz
  27. ;        machine. Slower machines will need a slightly lower value.
  28. ;
  29. ;        Special consideration has been taken to allow WHOA to be 
  30. ;        compatible with DOS versions earlier than 3.1, which do not
  31. ;        support the MULTIPLEX interrupt protocol.  WHOA uses the 
  32. ;        multiplex interrupt because it provides a broader range of 
  33. ;        software compatibility than do the eight conventional user 
  34. ;        interrupts.
  35. ;
  36. ;        For older versions of DOS, WHOA uses a user interrupt
  37. ;        (SLOW_INT) as the channel of communication to the resident
  38. ;        portion of WHAO!. To provide full compatibility, SLOW_INT
  39. ;        should be set to an interrupt that is not used by any of the 
  40. ;        software you wish to use WHOA with.
  41. ;
  42. ;------------------------------------------------------------------------------
  43.  
  44. ;------------------------------------------------------------------------------
  45. ;                E Q U A T E   A R E A
  46. ;------------------------------------------------------------------------------
  47. MULTI_INT        equ    2fh    ; Multiplex used if DOS >= 3.1.
  48. SLOW_INT        equ    60h    ; Interrupt used if DOS < 3.1,
  49.                     ; other possible values 60h-67h.
  50. KEYBOARD_INT        equ    16h    ; Keyboard Interrupt.
  51. TIME_INT        equ    08h    ; Time of Day Interrupt.
  52. MULTI_HANDLE        equ    89h    ; Our Unique Multiplex ID.
  53. INSTALL_CHECK        equ    0h    ; Code to check if resident.
  54. UNINSTALL        equ    1h    ; Code to uninstall WHOA!
  55. SET_LOOP        equ    2h    ; Code to set the delay loop.
  56.  
  57. STACK_SIZE        equ    100h    ; Size for our Stack.
  58.  
  59. HOT_KEY            equ    7100h    ; ALT F10.
  60. STROBE_PAUSE        equ    5    ; Strobe delay count setting.
  61. SET_MIN            equ    1    ; Minimum delay loop value.
  62. SET_MAX            equ    435    ; Maximum delay loop value.
  63. TIME_PAUSE        equ    100    ; Inner loop initial setting.
  64. LOOP_DEFAULT        equ    350    ; Delay default setting.
  65.  
  66. STROBE_BEGIN        equ    28    ; Column coordinate for strobe.
  67. STROBE_END        equ    37    ; Column coordinate for strobe.
  68.  
  69. SCREEN_SIZE        equ    16 * 1024 / 2 ; Size in words of largest
  70.                     ; screen to save.
  71.  
  72. BORDER            =    16+14    ; Border color.
  73. TEXT            =    16+15    ; Text color.
  74.  
  75. DOS_CMP            equ    0310h    ; DOS comparison constant.
  76.  
  77.             ;--- Return Codes ---
  78. NOT_INSTALLED        equ    0    ; WHOA not installed return code.
  79. INSTALLED        equ    0ffh    ; WHOA is installed return code.
  80. ERROR            equ    0feh    ; Error occurred
  81. OK            equ    0fdh    ; No errors occurred.
  82.  
  83. ;------------------------------------------------------------------------------
  84. ;                 M A C R O   A R E A
  85. ;------------------------------------------------------------------------------
  86.  
  87.             ;----------------------------------------------------
  88.             ;    DISPLAY_AT parm1,parm2,parm3,parm4 - Display
  89.             ;    message on screen starting at fixed 
  90.             ;    location. Does not use DOS.
  91.             ;
  92.             ;    parm1 = X coordinate of start of message.
  93.             ;    parm2 = Y coordinate of start of message.
  94.             ;    parm3 = Color of message.
  95.             ;    parm4 = String of character to display.
  96.             ;----------------------------------------------------
  97. DISPLAY_AT        macro    parm1,parm2,parm3,parm4
  98.             local    label1,label2
  99.             jmp    label2
  100. label1            db    parm4&,0
  101. label2:            mov    si,offset cseg:label1
  102.             mov    cx,&parm3
  103.             mov    ax,&parm2
  104.             mov    di,&parm1
  105.             call    display_at_proc
  106.             endm
  107.  
  108.             ;---------------------------------------------------
  109.             ;    DISPLAY_DOS parm1 - Display a message to the
  110.             ;    console. Uses DOS function 09h.
  111.             ;
  112.             ;    parm1 = Message to display.
  113.             ;---------------------------------------------------
  114. DISPLAY_DOS        macro    parm1
  115.             local    label1,label2
  116.             jmp    label2
  117. label1            db    parm1&,24h
  118. label2:            mov    dx,offset cseg:label1
  119.             mov    ah,09h
  120.             int    21h
  121.             endm
  122.  
  123.             ;-----------------------------------------------------
  124.             ;    RESIDENT parm1 - Executes the resident portion
  125.             ;    of WHOA! Determines which interrupt to 
  126.             ;    use (multiplex or user) based on DOS version.
  127.             ;
  128.             ;    parm1 - Resident function.
  129.             ;-----------------------------------------------------
  130. RESIDENT        macro    parm1
  131.             local    label1,label2
  132.             mov    al,&parm1
  133.             cmp    cs:dos_version,DOS_CMP    ; What DOS Version??
  134.             jae    label1            ; Jump if DOS >= 3.1.
  135.             call    call_interrupt
  136.             jmp    label2
  137. label1:            mov    ah,MULTI_HANDLE
  138.             int    MULTI_INT
  139. label2:
  140.             endm
  141.  
  142. ;------------------------------------------------------------------------------
  143. ;                C O D E   A R E A
  144. ;------------------------------------------------------------------------------
  145. cseg            segment    byte public 'code'
  146.             assume    cs:cseg,ds:cseg
  147.             org    100h
  148. slowdown        proc    far
  149.             jmp    slowdown_0010
  150.  
  151. ;------------------------------------------------------------------------------
  152. ;                T S R   D A T A   A R E A
  153. ;------------------------------------------------------------------------------
  154. dos_version        dw    ?    ; Version number of DOS.
  155. slow_interrupt        db    SLOW_INT ; Interrupt used if DOS < 3.1.
  156. old_multi_int        dd    ?    ; Address of old multiplex.
  157. old_keyboard_int    dd    ?    ; Address of old keyboard.
  158. old_time_int        dd    ?    ; Address of old time of day.
  159. activated        db    0    ; Switch to prevent re-entry.
  160. crt_stat        dw    ?    ; CRT port.
  161.  
  162. save_sp            dw    ?    ; Save area of old stack.
  163. save_ss            dw    ?    ; Save area of old stack.
  164. video_state        db    ?    ; Save video state.
  165. cursor_location        dw    ?    ; Save cursor location.
  166. current_page        db    ?    ; Save page.
  167. current_background    db    ?    ; Current background color.
  168. current_palette        db    ?
  169.  
  170. asc_value        db    6 dup(0) ; Area for number display.
  171. setting            dw    LOOP_DEFAULT ; Delay loop setting.
  172. delay_switch        db    1    ; Delay Switch on/off. 1 = ON.
  173.  
  174. strobe            dw    STROBE_BEGIN ; Location of strobe pattern.
  175. move_strobe1        dw    ?    ; Delay countdown.
  176. move_strobe2        dw    ?    ; Delay countdown.
  177. screen_seg        dw    ?    ; Address of screen.
  178. enable            db    1    ; Enable/disable switch.
  179. columns            db    80    ; Number of columns on screen.
  180.  
  181. ;------------------------------------------------------------------------------
  182. ;               T S R   C O D E   A R E A
  183. ;------------------------------------------------------------------------------
  184.  
  185.             ;-------------------------------------------------------
  186.             ;   multiplex - This procedure handles external
  187.             ;    communications for the memory resident
  188.             ;    portion of WHOA running on DOS 3.1 or 
  189.             ;    greater.
  190.             ;-------------------------------------------------------
  191. multiplex        proc    far
  192.  
  193.             ;--- Check for our unique handle. If not found ---
  194.             ;--- jump to next multiplex routine in chain. ----
  195.  
  196.             cmp    ah,MULTI_HANDLE 
  197.             je    multiplex_0010  
  198.             jmp    dword ptr cs:old_multi_int
  199. multiplex_0010:        call    request     
  200.             iret    ; Must return as an interrupt routine.
  201. multiplex        endp
  202.  
  203.             ;-------------------------------------------------
  204.             ;   interrupt - This procedure handles external
  205.             ;    communications for the memory resident
  206.             ;    portion of WHOA running on DOS 3.0 or
  207.             ;    less.
  208.             ;-------------------------------------------------
  209. interrupt        proc    far
  210.             call    request
  211.             iret
  212. interrupt        endp
  213.  
  214.             ;-----------------------------------------------
  215.             ;   request - This routine execute a slowdown
  216.             ;    function based on the contents of AL. 
  217.             ;
  218.             ;   Status returned in AL.
  219.             ;-----------------------------------------------
  220. request            proc    near
  221.             ;--- We are being called. Find out why ---
  222.  
  223. request_0010:        cmp    al,INSTALL_CHECK ; Check for installed?
  224.             jne    request_0020    ; Jump if NO.   
  225.             mov    al,INSTALLED    ; We are already installed.
  226.             ret
  227.  
  228. request_0020:        cmp    al,UNINSTALL    ; Ordered to uninstall?
  229.             jne    request_0030    ; Jump if NO.   
  230.             call    restore        ; Uninstall.
  231.             mov    al,OK        ; Everything went OK.
  232.             ret
  233.  
  234. request_0030:        cmp    al,SET_LOOP    ; Ordered to set delay loop?
  235.             jne    request_0040    ; Jump if NO.   
  236.             mov    cs:setting,bx    ; Set delay loop counter.
  237.             mov    al,OK        ; Everything went OK.
  238.             ret 
  239.  
  240. request_0040:        mov    al,ERROR    ; Unknown function.
  241.             ret   
  242. request            endp
  243.  
  244.             ;------------------------------------------------
  245.             ;   keyboard - This routine will compare every
  246.             ;    keystroke in search of our hot key.
  247.             ;    If <Alt> <F10> is pressed then send     
  248.             ;    WHOA!'s Menu into motion.
  249.             ;------------------------------------------------
  250. keyboard        proc    far
  251.             ;--- Check for READ KEYBOARD ---
  252.  
  253.             cmp    ah,0        ; Is it a keyboard request?
  254.             je    keyboard_0010    ; Jump if YES. 
  255.             jmp    dword ptr cs:old_keyboard_int ; Do original
  256.                         ; keyboard routine.
  257.             ;--- Check for hot key ---
  258.  
  259. keyboard_0010:        pushf            ; Call original keyboard.
  260.             call    dword ptr cs:old_keyboard_int
  261.             pushf
  262.             cmp    ax,HOT_KEY    ; Is key hot key?
  263.             je    keyboard_0020    ; Jump if YES.
  264.             popf
  265.             iret            ; Return to interrupter.
  266.  
  267.             ;--- Check to see if menu is already active. ---
  268.             ;--- If not, activate menu routine. ------------
  269.  
  270. keyboard_0020:        cli            ; Stop Interrupts 
  271.             cmp    cs:activated,0    ; Already active?
  272.             jne    keyboard_0030    ; Jump if YES. 
  273.  
  274.             mov    cs:activated,1    ; Set active flag.
  275.             call    active        ; Activate menu.
  276.             mov    cs:activated,0    ; Clear active flag.
  277.  
  278.             ;--- Contine on with next key --
  279.  
  280. keyboard_0030:        sti
  281.             mov    ah,0
  282.             popf
  283.             jmp    keyboard_0010
  284. keyboard        endp
  285.  
  286.             ;-----------------------------------------------------
  287.             ;    active - This procedure protects amd initializes
  288.             ;    the system and calls the menu.
  289.             ;-----------------------------------------------------
  290. active            proc    near
  291.             mov    cs:delay_switch,0 ; Turn off delay.
  292.  
  293.             push    ax
  294.             push    bx
  295.  
  296.             ;--- Setup our stack ---
  297.  
  298.             mov    cs:save_sp,sp
  299.             mov    cs:save_ss,ss
  300.             mov    ax,offset cseg:slowdown_0010 + STACK_SIZE
  301.             mov    bx,cs
  302.             cli
  303.             mov    ss,bx
  304.             mov    sp,ax
  305.             sti
  306.  
  307.             push    cx        ; Save registers.
  308.             push    dx
  309.             push    si
  310.             push    di
  311.             push    es
  312.             push    ds
  313.             push    bp
  314.  
  315.             mov    ax,cs
  316.             mov    ds,ax
  317.  
  318.             call    calibrate    ; Call menu calibrate.
  319.  
  320.             pop    bp        ; Restore registers.
  321.             pop    ds
  322.             pop    es
  323.             pop    di
  324.             pop    si
  325.             pop    dx
  326.             pop    cx
  327.  
  328.             ;--- Restore old stack ---
  329.  
  330.             cli
  331.             mov    ss,cs:save_ss
  332.             mov    sp,cs:save_sp
  333.             sti
  334.             pop    bx
  335.             pop    ax
  336.  
  337.             mov    al,cs:enable
  338.             mov    cs:delay_switch,al ; Turn on Delay.
  339.             ret
  340. active            endp
  341.  
  342.             ;-----------------------------------------------
  343.             ;    time_of_day - This procedure contains
  344.             ;    the loop that slows the system down.
  345.             ;    The original time_of_day routine is
  346.             ;    called to maintain the system's clock.
  347.             ;-----------------------------------------------
  348. time_of_day        proc    far
  349.  
  350.             ;--- Call original time_of_day routine ---
  351.  
  352.             pushf
  353.             call    dword ptr cs:old_time_int
  354.  
  355.             ;--- Check to see if we are disabled ---
  356.  
  357.             pushf
  358.             cmp    cs:delay_switch,0 ; Are we disabled?
  359.             je    time_of_day_0030 ; Jump if YES. 
  360.  
  361.             ;--- Do our loop to the current setting ---
  362.  
  363.             cli            ; Stop all interrupts.
  364.             push    cx
  365.             mov    cx,cs:setting
  366. time_of_day_0010:    push    cx
  367.             mov    cx,TIME_PAUSE
  368. time_of_day_0020:    loop    time_of_day_0020
  369.             pop    cx
  370.             loop    time_of_day_0010
  371.             pop    cx
  372.             sti            ; Restore interrupts.
  373.  
  374.             ;--- Return to caller ---
  375.  
  376. time_of_day_0030:    popf
  377.             iret
  378. time_of_day        endp
  379.  
  380.             ;---------------------------------------------------
  381.             ;   calibrate - This procedure displays WHOA!'s
  382.             ;    menu to allow calibration of the slowdown
  383.             ;    loop and allow disable/enable of WHOA!
  384.             ;---------------------------------------------------
  385. calibrate        proc    near
  386.             call    save_screen    ; Save the current screen.
  387.             mov    ax,setting    ; Initialize our strobe setting.
  388.             mov    move_strobe1,ax
  389.             mov    move_strobe2,TIME_PAUSE / 10
  390.  
  391.             ;--- Display menu using macro ---
  392.  
  393.     DISPLAY_AT 00,00,BORDER,<'╔════════════════════════════════════════╗'>
  394.     DISPLAY_AT 00,01,BORDER,186
  395.     DISPLAY_AT 01,01,TEXT,   <'        WHOA! by Brad Crandall           '>
  396.     DISPLAY_AT 41,01,BORDER,186
  397.     DISPLAY_AT 00,02,BORDER,<'╟────────────────────────────────────────╢'>
  398.     DISPLAY_AT 00,03,BORDER,<186>
  399.     DISPLAY_AT 01,03,TEXT,   <'       Key          Meaning             '>
  400.     DISPLAY_AT 41,03,BORDER,<186>
  401.     DISPLAY_AT 00,04,BORDER,<186>
  402.     DISPLAY_AT 01,04,TEXT,   <'     -------    ----------------        '>
  403.     DISPLAY_AT 41,04,BORDER,<186>
  404.     DISPLAY_AT 00,05,BORDER,<186>
  405.     DISPLAY_AT 01,05,TEXT,   <'        +       Execute Faster          '>
  406.     DISPLAY_AT 41,05,BORDER,<186>
  407.     DISPLAY_AT 00,06,BORDER,<186>
  408.     DISPLAY_AT 01,06,TEXT,   <'        -       Execute Slower          '>
  409.     DISPLAY_AT 41,06,BORDER,<186>
  410.     DISPLAY_AT 00,07,BORDER,<186>
  411.     DISPLAY_AT 01,07,TEXT,   <'        D       Disable WHOA!           '>
  412.     DISPLAY_AT 41,07,BORDER,<186>
  413.     DISPLAY_AT 00,08,BORDER,<186>
  414.     DISPLAY_AT 01,08,TEXT,   <'        E       Enable WHOA!            '>
  415.     DISPLAY_AT 41,08,BORDER,<186>
  416.     DISPLAY_AT 00,09,BORDER,<186>
  417.     DISPLAY_AT 01,09,TEXT,   <'     <Enter>    Quit                    '>
  418.     DISPLAY_AT 41,09,BORDER,<186>
  419.     DISPLAY_AT 00,10,BORDER,<186>
  420.     DISPLAY_AT 01,10,TEXT,   <'                                        '>
  421.     DISPLAY_AT 41,10,BORDER,<186>
  422.     DISPLAY_AT 00,11,BORDER,<186>
  423.     DISPLAY_AT 01,11,TEXT,   <' Current setting 00000   ->          <- '>
  424.     DISPLAY_AT 41,11,BORDER,<186>
  425.     DISPLAY_AT 00,12,BORDER,<186>
  426.     DISPLAY_AT 01,12,TEXT,   <'         Press <ENTER> to quit.         '>
  427.     DISPLAY_AT 41,12,BORDER,<186>
  428.     DISPLAY_AT 00,13,BORDER,<'╚════════════════════════════════════════╝'>
  429.             ;--- Display delay loop number ---
  430.  
  431. calibrate_0005:        call    display_number
  432.  
  433.             ;--- Move fancy strobe ---
  434.  
  435. calibrate_0010:        dec    move_strobe2
  436.             jne    calibrate_0035
  437.             mov    move_strobe2,TIME_PAUSE / 10
  438.             dec    move_strobe1
  439.             jne    calibrate_0035
  440.             mov    ax,setting
  441.             mov    move_strobe1,ax
  442.             DISPLAY_AT strobe,11,TEXT,<' '>
  443.             cmp    strobe,STROBE_END
  444.             je    calibrate_0020
  445.             inc    strobe
  446.             jmp    calibrate_0030
  447. calibrate_0020:        mov    strobe,STROBE_BEGIN
  448. calibrate_0030:        cmp    enable,0
  449.             je    calibrate_0035
  450.             DISPLAY_AT strobe,11,BORDER,<'*'>
  451.  
  452.             ;--- Get data from keyboard if available ---
  453.  
  454. calibrate_0035:        mov    ah,1
  455.             pushf
  456.             call    dword ptr old_keyboard_int ; Original.
  457.             je    calibrate_0010    ; Jump if no data.
  458.             mov    ah,0
  459.             pushf
  460.             call    dword ptr old_keyboard_int ; Original.
  461.  
  462.             ;--- Calibrate based on character received ---
  463.  
  464.             cmp    al,'+'        ; Do we speed up?
  465.             jne    calibrate_0040    ; Jump if NO.  
  466.             dec    setting        ; Speed up loop.
  467.             cmp    setting,SET_MIN    ; Less than minimum setting?
  468.             jae    calibrate_0038    ; Jump if NO.  .
  469.             mov    setting,SET_MIN    ; Set to minimum setting.
  470. calibrate_0038:        jmp    calibrate_0005
  471.  
  472. calibrate_0040:        cmp    al,'-'        ; Do we slow down?
  473.             jne    calibrate_0050    ; Jump if NO.   
  474.             inc    setting        ; Slow down.
  475.             cmp    setting,SET_MAX    ; Greater than maximum?
  476.             jbe    calibrate_0045    ; Jump if NO.   
  477.             mov    setting,SET_MAX    ; Set to maximum setting.
  478. calibrate_0045:        jmp    calibrate_0005
  479.  
  480. calibrate_0050:        cmp    al,13        ; Are we done?
  481.             je    calibrate_0060    ; Jump if YES. 
  482.  
  483.             cmp    al,'d'        ; Disable WHOA!?
  484.             je    calibrate_0051    ; Jump if YES. 
  485.             cmp    al,'D'        ; Disable WHOA!?
  486.             jne    calibrate_0052    ; Jump if NO. 
  487. calibrate_0051:        mov    enable,0    ; Disable WHOA!
  488.             jmp    calibrate_0010
  489.  
  490. calibrate_0052:        cmp    al,'e'        ; Enable WHOA!?
  491.             je    calibrate_0053    ; Jump if YES.
  492.             cmp    al,'E'        ; Enable WHOA!?
  493.             jne    calibrate_0055    ; Jump if NO.   
  494. calibrate_0053:        mov    enable,1    ; Enable WHOA!
  495.  
  496. calibrate_0055:        jmp    calibrate_0010
  497.  
  498. calibrate_0060:        call    restore_screen    ; Restore the screen.
  499.             ret
  500. calibrate        endp
  501.  
  502.             ;--------------------------------------------------
  503.             ;    display_number - This procedure will display
  504.             ;    the current setting of the delay loop at
  505.             ;    the expected position on the screen.
  506.             ;--------------------------------------------------
  507. display_number        proc    near
  508.             mov    ax,setting
  509.             call    numasc        ; Convert to an ASCII number.
  510.  
  511.             mov    di,18        ; Set parameters.
  512.             mov    ax,11
  513.             mov    cx,TEXT
  514.             mov    si,offset cseg:asc_value
  515.  
  516.             call    display_at_proc ; Display ASCII number.
  517.  
  518.             ret
  519. display_number        endp
  520.  
  521.             ;--------------------------------------------------
  522.             ;    save_screen - This procedure will save the
  523.             ;    current screen.  Care is taken so that
  524.             ;    not only the data on the screen is saved,
  525.             ;    but the current mode, color, and palette
  526.             ;    is saved also. This allows WHOA to
  527.             ;    work under graphic conditions.
  528.             ;--------------------------------------------------
  529. save_screen        proc    near
  530.             push    ds
  531.             push    es
  532.  
  533.             ;--- Get screen information ---
  534.  
  535.             xor    ax,ax
  536.             mov    es,ax
  537.             mov    ax,es:[463h]
  538.             add    ax,6
  539.             mov    cs:crt_stat,ax    ; CRT status port address.
  540.  
  541.             mov    screen_seg,0b800h ; Screen address.
  542.             xor    ax,ax
  543.             mov    es,ax
  544.             mov    ax,word ptr es:[0410h]
  545.             and    al,30h
  546.             cmp    al,30h
  547.             jne    save_screen_0010
  548.             mov    screen_seg,0b000h ; Screen address.
  549.  
  550.             ;--- Save the screen to a safe area ---
  551.  
  552. save_screen_0010:    call    screen_off    ; Turn off the display.
  553.             mov    di,offset cseg:slowdown_0010 + STACK_SIZE + 1
  554.             mov    ax,cs
  555.             mov    es,ax
  556.             mov    ds,screen_seg
  557.             xor    si,si
  558.             mov    cx,SCREEN_SIZE
  559.             cld
  560.             rep    movsw
  561.             call    screen_on    ; Turn on the display.
  562.  
  563.             ;--- Get current Video State ---
  564.  
  565.             mov    ah,15
  566.             int    10h
  567.             mov    cs:video_state,al
  568.             mov    cs:current_page,bh
  569.             mov    ah,03
  570.             int    10h
  571.             mov    cs:cursor_location,dx
  572.  
  573.             ;--- Get current background color and palette ---
  574.  
  575.             push    es
  576.             xor    ax,ax
  577.             mov    es,ax
  578.             mov    al,es:[466h]    ; CRT_Palette.
  579.             and    al,1fh
  580.             mov    cs:current_background,al
  581.             mov    cs:current_palette,0
  582.             mov    al,es:[466h]
  583.             and    al,20h
  584.             je    save_screen_0015
  585.             mov    cs:current_palette,1
  586. save_screen_0015:    pop    es
  587.  
  588.             ;--- Change Video State to an 80 X 25 mode ---
  589.             ;--- if necessary. ---------------------------
  590.  
  591.             cmp    cs:video_state,3
  592.             jbe    save_screen_0030
  593.             cmp    cs:video_state,7
  594.             je    save_screen_0030
  595.  
  596.             mov    ah,0
  597.             mov    al,3
  598.             cmp    cs:screen_seg,0b800h
  599.             je    save_screen_0020
  600.             mov    al,2
  601. save_screen_0020:    int    10h        ; Change mode to 80 X 25.
  602.  
  603.             ;--- Move cursor off screen ---
  604.  
  605. save_screen_0030:    mov    bh,cs:current_page
  606.             mov    dx,0ffffh
  607.             mov    ah,2
  608.             int    10h
  609.  
  610.             pop    es
  611.             pop    ds
  612.             ret
  613. save_screen        endp
  614.  
  615.             ;----------------------------------------------------
  616.             ;    restore_screen - This procedure will restore
  617.             ;    the screen to the state it was in prior to
  618.             ;    the save_screen call.
  619.             ;----------------------------------------------------
  620. restore_screen        proc    near
  621.             push    ds
  622.             push    es
  623.  
  624.             ;--- Restore Video State ---
  625.  
  626.             cmp    video_state,3
  627.             jbe    restore_0010
  628.             cmp    video_state,7
  629.             je    restore_0010
  630.  
  631.             mov    al,video_state
  632.             mov    ah,0
  633.             int    10h
  634.  
  635.             ;--- Restore Background color and palette ---
  636.  
  637.             mov    ah,11
  638.             mov    bl,current_palette
  639.             mov    bh,1
  640.             int    10h
  641.  
  642.             mov    ah,11
  643.             mov    bl,current_background
  644.             mov    bh,0
  645.             int    10h
  646.  
  647.             ;--- Reset Cursor Position ---
  648.  
  649. restore_0010:        mov    bh,current_page
  650.             mov    dx,cursor_location
  651.             mov    ah,2
  652.             int    10h
  653.  
  654.             ;--- Restore screen ---
  655.  
  656.             call    screen_off    ; Turn off the display.
  657.             mov    si,offset cseg:slowdown_0010 + STACK_SIZE + 1
  658.             mov    es,screen_seg
  659.             xor    di,di
  660.             mov    cx,SCREEN_SIZE
  661.             cld
  662.             rep    movsw
  663.             call    screen_on    ; Turn on the display.
  664.  
  665.             pop    es
  666.             pop    ds
  667.             ret
  668. restore_screen        endp
  669.  
  670.             ;----------------------------------------------------
  671.             ;    display_at_proc - This procedure is used by
  672.             ;    the macro "DISPLAY_AT".  This procedure
  673.             ;    will display a string to a given location
  674.             ;    anywhere on the screen.
  675.             ;----------------------------------------------------
  676. display_at_proc        proc    near
  677.             mov    es,screen_seg
  678.             mul    columns
  679.             add    di,ax
  680.             shl    di,1        ; Screen offset.
  681.             mov    ah,cl        ; Color in ah.
  682. display_at_proc_0010:    mov    al,byte ptr cs:[si]
  683.             inc    si
  684.             cmp    al,0        ; Are we done?
  685.             je    display_at_proc_0050 ; Jump if YES. 
  686.  
  687.             ;--- Wait for horizontal retrace if CGA ---
  688.  
  689.             cmp    screen_seg,0b800h
  690.             jne    display_at_proc_0040
  691.             push    ax
  692.             mov    dx,crt_stat
  693.             cli
  694. display_at_proc_0020:    in    al,dx
  695.             test    al,1
  696.             jne    display_at_proc_0020
  697. display_at_proc_0030:    in    al,dx
  698.             test    al,1
  699.             je    display_at_proc_0030
  700.             pop    ax
  701.  
  702.             ;--- Display one byte of string ---
  703.  
  704. display_at_proc_0040:    stosw
  705.             sti
  706.             jmp    display_at_proc_0010
  707.  
  708. display_at_proc_0050:    ret
  709. display_at_proc        endp
  710.  
  711. ;------------------------------------------------------------------------------
  712. ;    NUMASC.INC    By Brad D Crandall    Created 04/23/85          |
  713. ;------------------------------------------------------------------------------
  714. ;                                          |
  715. ;    numasc:                                      |
  716. ;                                          |
  717. ;    This procedure contains the code necessary to convert a              |
  718. ;    binary value into an ASCII coded number.  This routine will not          |
  719. ;    work correctly on numbers larger than 65536. The numbers          |
  720. ;    must be integers greater than or equal to 0.                  |
  721. ;                                          |
  722. ;    How to use:                                  |
  723. ;                                          |
  724. ;        1) The following variables must be defined in your DS segment.    |
  725. ;                                          |
  726. ;        asc_value    DB    5 DUP(?)                  |
  727. ;                                          |
  728. ;        2) Load the AX register with the binary value.              |
  729. ;                                          |
  730. ;        3) Upon return, ASC_VALUE will contain the ASCII Coded number.    |
  731. ;                                          |
  732. ;        4) All registers are preserved except AX.                  |
  733. ;                                          |
  734. ;------------------------------------------------------------------------------
  735. base_ten        dw    10000,1000,100,10,1
  736. numasc            proc    near
  737.             push    si
  738.             push    cx
  739.             push    dx
  740.             push    di
  741.             xor    si,si
  742.             xor    di,di
  743.             mov    cx,5
  744. numasc_0005:        mov    dl,0
  745. numasc_0010:        sub    ax,cs:base_ten[si]
  746.             jb    numasc_0020
  747.             inc    dl
  748.             jmp    numasc_0010
  749. numasc_0020:        add    ax,cs:base_ten[si]
  750.             add    dl,'0'
  751.             mov    asc_value[di],dl
  752.             add    si,2
  753.             inc    di
  754.             loop    numasc_0005
  755.             pop    di
  756.             pop    dx
  757.             pop    cx
  758.             pop    si
  759.             ret
  760. numasc            endp
  761.  
  762.             ;-------------------------------------------------
  763.             ;    restore - This procedure will restore the
  764.             ;    interrupt vectors to the state that they
  765.             ;    were in prior to the execution of WHOA! 
  766.             ;    This terminates WHOA completely.
  767.             ;-------------------------------------------------
  768. restore            proc    near
  769.             push    ax
  770.             push    bx
  771.  
  772.             ;--- Setup our stack ---
  773.  
  774.             mov    cs:save_sp,sp
  775.             mov    cs:save_ss,ss
  776.             mov    ax,offset cseg:slowdown_0010 + STACK_SIZE
  777.             mov    bx,cs
  778.             cli
  779.             mov    ss,bx
  780.             mov    sp,ax
  781.             sti
  782.  
  783.             push    cx
  784.             push    dx
  785.             push    si
  786.             push    di
  787.             push    es
  788.             push    ds
  789.             push    bp
  790.  
  791.             ;--- Restore time_of_day interrupt to the original ---
  792.  
  793.             mov    dx,cs:word ptr cs:old_time_int
  794.             mov    ds,cs:word ptr cs:old_time_int+2
  795.             mov    ah,25h
  796.             mov    al,TIME_INT
  797.             int    21h
  798.  
  799.             ;--- Restore multiplex/slow interrupt to original state ---
  800.  
  801.             mov    dx,cs:word ptr cs:old_multi_int
  802.             mov    ds,cs:word ptr cs:old_multi_int+2
  803.             mov    ah,25h
  804.             mov    al,MULTI_INT
  805.             cmp    cs:dos_version,DOS_CMP
  806.             jae    restore2_0010
  807.             mov    al,cs:slow_interrupt
  808. restore2_0010:        int    21h
  809.  
  810.             ;--- Restore keyboard interrupt to the original ---
  811.  
  812.             mov    dx,cs:word ptr cs:old_keyboard_int
  813.             mov    ds,cs:word ptr cs:old_keyboard_int+2
  814.             mov    ah,25h
  815.             mov    al,KEYBOARD_INT
  816.             int    21h
  817.  
  818.             pop    bp
  819.             pop    ds
  820.             pop    es
  821.             pop    di
  822.             pop    si
  823.             pop    dx
  824.             pop    cx
  825.  
  826.             ;--- Restore old stack ---
  827.  
  828.             cli
  829.             mov    ss,cs:save_ss
  830.             mov    sp,cs:save_sp
  831.             sti
  832.             pop    bx
  833.             pop    ax
  834.             ret
  835. restore            endp
  836.  
  837.             ;---------------------------------------
  838.             ;    screen-off - This procedure will
  839.             ;    turn off the display. We do
  840.             ;    this so we don't have to see
  841.             ;    CGA snow during massive direct
  842.             ;    screen I/O.
  843.             ;---------------------------------------
  844. screen_off        proc    near
  845.             push    ds
  846.             xor    ax,ax
  847.             mov    ds,ax
  848.             mov    al,byte ptr ds:[0465h]
  849.             and    al,0f7h
  850.             mov    dx,03d8h
  851.             out    dx,al
  852.             pop    ds
  853.             ret
  854. screen_off        endp
  855.  
  856.             ;--------------------------------------
  857.             ;    screen_on - This procedure will 
  858.             ;    turn on the display.
  859.             ;--------------------------------------
  860. screen_on        proc    near
  861.             push    ds
  862.             xor    ax,ax
  863.             mov    ds,ax
  864.             mov    al,byte ptr ds:[0465h]
  865.             or    al,08h
  866.             out    dx,al
  867.             pop    ds
  868.             ret
  869. screen_on        endp
  870.  
  871. ;------------------------------------------------------------------------------
  872. ;           N O N - R E S I D E N T   D A T A   A R E A
  873. ;------------------------------------------------------------------------------
  874. valid_parm        db    0    ; Switch to determine valid.
  875.  
  876. ;------------------------------------------------------------------------------
  877. ;           N O N - R E S I D E N T   C O D E   A R E A
  878. ;------------------------------------------------------------------------------
  879.  
  880.             ;--- First major code to be executed within WHOA ---
  881.  
  882. slowdown_0010:        push    ds
  883.             xor    ax,ax
  884.             push    ax
  885.             mov    ax,cs
  886.             mov    ds,ax
  887.  
  888.             ;--- Get the DOS version number ---
  889.             mov    ah,30h        ; Get DOS Version number.
  890.             int    21h
  891.             xchg    al,ah
  892.             mov    dos_version,ax    ; High is MAJOR, Low is MINOR.
  893.  
  894.             call    set_loop_default ; Set the loop default counter.
  895.  
  896. DISPLAY_DOS <'Copyright 1988 COMPUTE! Publications, Inc.  All rights reserved.',13,10>
  897.  
  898.             ;--- Determine whether to install or unload ---
  899.  
  900.             cmp    dos_version,DOS_CMP ; What DOS Version??
  901.             jae    slow_down_00105    ; Jump if DOS >= 3.1.
  902.             call    check_installed
  903.             jmp    slow_down_00106
  904. slow_down_00105:    mov    ah,MULTI_HANDLE
  905.             mov    al,INSTALL_CHECK    
  906.             int    MULTI_INT
  907. slow_down_00106:    cmp    al,NOT_INSTALLED ; Are we installed?
  908.             jne    slow_down_0011     ; Jump if NO.
  909.             jmp    slow_down_0019
  910.  
  911.             ;--- If a parameter was passed, then send ----------
  912.             ;--- it to the memory resident part of WHOA! ---
  913.             ;--- otherwise disable WHOA completely. --------
  914.  
  915. slow_down_0011:        cmp    valid_parm,0    ; Any parm?
  916.             jne    slow_down_00113    ; Jump if YES. 
  917.             jmp    slow_down_0013
  918.  
  919. slow_down_00113:    cmp    valid_parm,1    ; Was it a valid parm?
  920.             je    slow_down_00115    ; Jump if YES. 
  921.  
  922.             DISPLAY_DOS <'Parameter out of range.',10,13>
  923.             DISPLAY_DOS <'Valid values are between 1 and 435.',10,13>
  924.             ret            ; Return to DOS.
  925.  
  926.             ;--- Pass new delay loop value to TSR WHOA ---
  927.  
  928. slow_down_00115:    mov    bx,setting
  929.             RESIDENT SET_LOOP
  930.             cmp    al,OK
  931.             jne    slow_down_0012
  932.             DISPLAY_DOS <'Setting completed.',10,13>
  933.             ret            ; Return to DOS.
  934. slow_down_0012:        DISPLAY_DOS <'TSR is unable to set.',10,13>
  935.             ret            ; Return to DOS.
  936.  
  937.             ;--- Uninstall WHOA ---
  938.  
  939. slow_down_0013:        RESIDENT UNINSTALL
  940.             cmp    al,OK        ; All go well?
  941.             jne    slow_down_0015    ; Jump if NO.   
  942.             DISPLAY_DOS <'Uninstalled.',10,13>
  943.             ret            ; Return to DOS.
  944. slow_down_0015:        DISPLAY_DOS <'Unable to uninstall.',10,13>
  945.             ret            ; Return to DOS.
  946.  
  947.             ;--- Install proper entry interrupt ---
  948.             ;--- based on DOS Version. ---
  949.  
  950. slow_down_0019:        cmp    dos_version,DOS_CMP
  951.             jae    slow_down_0020
  952.  
  953.             mov    al,slow_interrupt ; Install SLOW_INT.
  954.             mov    ah,35h
  955.             int    21h        ; Save original.
  956.             mov    word ptr old_multi_int,bx
  957.             mov    word ptr old_multi_int+2,es
  958.             mov    al,slow_interrupt   
  959.             mov    dx,offset cseg:interrupt
  960.             mov    ah,25h
  961.             int    21h
  962.             jmp    slow_down_0030
  963.  
  964. slow_down_0020:        mov    al,MULTI_INT    ; Install MULTI_INT
  965.             mov    ah,35h
  966.             int    21h        ; Save original.
  967.             mov    word ptr old_multi_int,bx
  968.             mov    word ptr old_multi_int+2,es
  969.             mov    dx,offset cseg:multiplex
  970.             mov    al,MULTI_INT
  971.             mov    ah,25h
  972.             int    21h
  973.  
  974.             ;--- Install Keyboard ---
  975.  
  976. slow_down_0030:        mov    al,KEYBOARD_INT
  977.             mov    ah,35h
  978.             int    21h        ; Save original.
  979.             mov    word ptr old_keyboard_int,bx
  980.             mov    word ptr old_keyboard_int+2,es
  981.  
  982.             mov    dx,offset cseg:keyboard
  983.             mov    al,KEYBOARD_INT
  984.             mov    ah,25h
  985.             int    21h
  986.  
  987.             ;--- Install time_of_day ---
  988.  
  989.             mov    al,TIME_INT
  990.             mov    ah,35h
  991.             int    21h        ; Save original.
  992.             mov    word ptr old_time_int,bx
  993.             mov    word ptr old_time_int+2,es
  994.  
  995.             mov    dx,offset cseg:time_of_day
  996.             mov    al,TIME_INT
  997.             mov    ah,25h
  998.             int    21h
  999.  
  1000.             ;--- Terminate and stay resident ---
  1001.  
  1002.             DISPLAY_DOS <'Now resident.  Use <Alt> F10 for calibrating.',13,10>
  1003.             mov    ax,cs
  1004.             mov    es,ax
  1005.             add    sp,4        ; Clean up stack.
  1006.             mov    dx,offset cseg:slowdown_0010 + STACK_SIZE + SCREEN_SIZE + SCREEN_SIZE + 2
  1007.             int    27h
  1008. slowdown        endp
  1009.  
  1010.             ;-----------------------------------------------------
  1011.             ;    set_loop_default - This procedure will examine
  1012.             ;    the command parameters in the Program 
  1013.             ;    Segment Prefix for any Loop Default Value.
  1014.             ;
  1015.             ;    Returns:
  1016.             ;    valid_parm is set as follows:
  1017.             ;        0 = No parameter given.
  1018.             ;        1 = Valid numeric parameter found.
  1019.             ;        2 = Valid parameter found.
  1020.             ;-----------------------------------------------------
  1021. set_loop_default    proc    near
  1022.             cmp    byte ptr es:[80h],0
  1023.             je    set_loop_default_0900
  1024.  
  1025.             ;--- Set numeric as loop default ---
  1026.  
  1027.             mov    di,81h        ; Offset of parms.
  1028.             mov    word ptr value,0
  1029.             xor    ch,ch
  1030.             mov    cl,byte ptr es:[80h]
  1031.  
  1032. set_loop_default_0010:    mov    al,byte ptr es:[di]
  1033.             inc    di
  1034.             cmp    al,'0'
  1035.             jb    set_loop_default_0020
  1036.             cmp    al,'9'
  1037.             ja    set_loop_default_0020
  1038.             call    ascnum
  1039. set_loop_default_0020:    loop    set_loop_default_0010
  1040.  
  1041.             mov    valid_parm,2    ; Set for invalid parm.
  1042.  
  1043.             ;--- Check numeric value to see if it is in range ---
  1044.  
  1045.             cmp    value,SET_MIN
  1046.             jb    set_loop_default_0910
  1047.             cmp    value,SET_MAX
  1048.             ja    set_loop_default_0910
  1049.  
  1050.             ;--- Valid value found ---
  1051.  
  1052.             mov    ax,value
  1053.             mov    setting,ax
  1054.             mov    valid_parm,1
  1055.             ret
  1056.  
  1057.             ;--- No Value Found ---
  1058.  
  1059. set_loop_default_0900:    mov    valid_parm,0
  1060. set_loop_default_0910:    ret
  1061. set_loop_default    endp
  1062.  
  1063.             ;------------------------------------------------
  1064.             ;    check_installed - This procedure will check
  1065.             ;    to see if WHOA is already resident.
  1066.             ;    This procedure is used on DOS versions
  1067.             ;    less than 3.1.
  1068.             ;
  1069.             ;    Returns in AL:
  1070.             ;    NOT_INSTALLED
  1071.             ;    INSTALLED
  1072.             ;------------------------------------------------
  1073. check_installed        proc    near
  1074.             push    es
  1075.  
  1076.             mov    ah,35h
  1077.             mov    al,slow_interrupt
  1078.             int    21h
  1079.  
  1080.             mov    al,INSTALLED
  1081.  
  1082.             ;--- Check to see routines match ---
  1083.             mov    si,offset cseg:interrupt
  1084.             mov    di,bx
  1085.             mov    cx,10        ; Number of bytes to compare.
  1086.             rep    cmpsb
  1087.             je    check_installed_0010
  1088.             mov    al,NOT_INSTALLED
  1089.  
  1090. check_installed_0010:    pop    es
  1091.             ret
  1092. check_installed        endp
  1093.  
  1094.             ;-----------------------------------------------
  1095.             ;    call_interrupt - This procedure calls the
  1096.             ;    resident portion of WHOA for DOS
  1097.             ;-----------------------------------------------
  1098. call_interrupt        proc    near
  1099.             int    SLOW_INT
  1100.             ret
  1101. call_interrupt        endp
  1102.  
  1103. ;------------------------------------------------------------------------------
  1104. ;    ASCNUM.INC    By Brad D Crandall    Created 04/23/85          |
  1105. ;------------------------------------------------------------------------------
  1106. ;                                          |
  1107. ;    ascnum:                                      |
  1108. ;                                          |
  1109. ;    This procedure contains the code necessary to convert a              |
  1110. ;    ASCII coded number into a binary value.  This routine will not          |
  1111. ;    work correctly on numbers larger than 65536. The number              |
  1112. ;    must be integers greater than or equal to 0.                  |
  1113. ;                                          |
  1114. ;    How to use:                                  |
  1115. ;                                          |
  1116. ;        1) Move a zero to VALUE.                          |
  1117. ;                                          |
  1118. ;        MOV    VALUE,0                              |
  1119. ;                                          |
  1120. ;        2) Load the AL register with the left most digit and then call    |
  1121. ;           this routine. Do this for every digit in the number.          |
  1122. ;                                          |
  1123. ;        MOV    AL,'3'        ; Translate 34 into binary.          |
  1124. ;        CALL    NUMASC                              |
  1125. ;                                          |
  1126. ;        MOV    AL,'4'                              |
  1127. ;        CALL    NUMASC                              |
  1128. ;                                          |
  1129. ;        3) The binary value will be found in the variable VALUE          |
  1130. ;                                          |
  1131. ;        MOV    AX,VALUE    ; AX now contains binary 34.          |
  1132. ;                                          |
  1133. ;        4) All registers are preserved except AX.                  |
  1134. ;------------------------------------------------------------------------------
  1135. ten            dw    10
  1136. value            dw    ?
  1137.  
  1138. ascnum            proc    near
  1139.             push    dx
  1140.             cmp    al,'0'
  1141.             jb    ascnum_0010     ; Not a number.
  1142.             cmp    al,'9'
  1143.             ja    ascnum_0010    ; Not a number.
  1144.             sub    al,'0'
  1145.             xor    ah,ah
  1146.             mov    dx,ax
  1147.             push    dx
  1148.             mov    ax,cs:value
  1149.             mul    cs:ten
  1150.             pop    dx
  1151.             add    ax,dx
  1152.             mov    cs:value,ax
  1153. ascnum_0010:        pop    dx
  1154.             ret
  1155. ascnum            endp
  1156.  
  1157. cseg            ends
  1158.             end    slowdown
  1159.